package com.ge.demo.restfulws.client;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

public class RestfulClientTemplate {

	private String baseURL;

	/**
	 * The current Path Variable
	 * 
	 * @see <blockquote>setBaseURL(String baseURL)</blockquote>
	 * @return null if the current path variable is not defined
	 */
	public String getBaseURL() {
		return baseURL;
	}

	/**
	 * The Base URL is the part of the Web Service location that doesn't change
	 * among different calls.
	 * 
	 * For example: http://localhost:8080/ServiceName
	 * 
	 * If the Base URL is not specified (is null) the URL specified in each call
	 * will be considered the complete URL. Such as:
	 * 
	 * http://localhost:8080/ServiceName/operationName/operationName/
	 * pathVariable1/...
	 * 
	 * @param baseURL
	 */
	public void setBaseURL(String baseURL) {
		this.baseURL = baseURL;
	}

	public RestfulClientTemplate(String baseURL) {
		this.baseURL = baseURL;
	}

	/**
	 * Makes a POST operation that returns a resulting object
	 * 
	 * @param endpointURL
	 * @param resultType
	 * @return
	 * @throws RestClientException
	 */
	public <T> T post(String endpointURL, Class<T> resultType)
			throws RestClientException {
		try {
			ResponseEntity<T> responseEntity = postForEntity(endpointURL,
					resultType);
			return resultType.cast(responseEntity.getBody());
		} catch (RestClientException e) {
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 * POST & GET operations
	 * 
	 * @param operationURL
	 * @param resultType
	 * @return
	 */
	public <T> ResponseEntity<T> postForEntity(final String operationURL,
			final Class<T> resultType) {

		final String completeURL = getCompleteURL(operationURL);

		HttpHeaders headers = getSecureHeaders("john_doe", "passwd123");
		RestTemplate restTemplate = getRestTemplate();
		HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
		ResponseEntity<T> responseEntity = restTemplate.postForEntity(
				completeURL, requestEntity, resultType);
		return responseEntity;
	}

	/**
	 * PUT operation
	 * 
	 * @param operationURL
	 * @param resultType
	 * @return
	 */
	public void put(final String operationURL, Object object) {
		final String completeURL = getCompleteURL(operationURL);
		RestTemplate restTemplate = getRestTemplate();
		restTemplate.put(completeURL, object);
	}
	
	/**
	 * DELETE operation
	 * 
	 * @param operationURL
	 * @param resultType
	 * @return
	 */
	public void delete(final String operationURL) {
		final String completeURL = getCompleteURL(operationURL);
		RestTemplate restTemplate = getRestTemplate();
		restTemplate.delete(completeURL);
	}

	/**
	 * GET operation
	 * 
	 * @param operationURL
	 * @param resultType
	 * @return
	 */
	public <T> ResponseEntity<T> get(final String operationURL,
			final Class<T> resultType) {
		final String completeURL = getCompleteURL(operationURL);

		RestTemplate restTemplate = getRestTemplate();
		ResponseEntity<T> responseEntity = restTemplate.getForEntity(
				completeURL, resultType);
		return responseEntity;
	}

	/**
	 * Concatenates the base and a specific-operation URLs 
	 * 
	 * @param endpointURL
	 * @return
	 */
	private String getCompleteURL(final String endpointURL) {
		return this.baseURL == null ? endpointURL : this.baseURL + endpointURL;
	}

	/**
	 * Creates a REST template able to convert a JSon response into
	 * a Java object.
	 * 
	 * @return
	 */
	private RestTemplate getRestTemplate() {
		RestTemplate restTemplate = new RestTemplate();
		List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
		converters.add(new MappingJacksonHttpMessageConverter());
		restTemplate.setMessageConverters(converters);
		return restTemplate;
	}

	/**
	 * Configures the default headers for consuming a JSON-based RESTful
	 * Web Service.
	 * 
	 * @return
	 */
	private HttpHeaders getHeaders() {
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
		return headers;
	}
	
	/**
	 * The same headers as getHeaders() but with additional
	 * Http Auth BASIC and the encoded authorization String.
	 * 
	 * @param userName
	 * @param password
	 * @return
	 */
	private HttpHeaders getSecureHeaders(String userName, String password) {
		HttpHeaders headers = getHeaders();

		String auth = userName.trim()+":"+password.trim();
		byte[] encodedAuthorization = Base64.encode(auth.getBytes());
		headers.add("Authorization", "Basic " + new String(encodedAuthorization));
		
		return headers;
	}

}
